function createElement(type, props, ...children) {
  // console.log(type, props, children)
  return {
    type,
    props: {
      ...props,
      children: children.map(child =>
        typeof child === "object" ? child : createTextElement(child)
      )
    }
  };
}

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: []
    }
  };
}

function render(element, container) {
  wipRoot = { //fiber 任务
    dom: container,
    props: {
      children: [element]
    },
    alternate: currentRoot
  }

  deletions = []
  nextUnitOfWork = wipRoot
  // const dom =
  //   element.type == "TEXT_ELEMENT"
  //     ? document.createTextNode("")
  //     : document.createElement(element.type);
  // const isProperty = key => key !== "children";
  // Object.keys(element.props)
  //   .filter(isProperty)
  //   .forEach(name => {
  //     dom[name] = element.props[name];
  //   });
  // element.props.children.forEach(child => render(child, dom));
  // container.appendChild(dom);
}


let nextUnitOfWork = null
let wipRoot = null  // 正在进行的节点
let currentRoot = null  // 保存上次的树
let deletions = []

function workLoop(deadline) {
  let shouldYield = false
  // nextUnitOfWork 第一次有值 ， 一个虚拟dom就有一个fiber任务 ， dom树对应fiber树
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
    shouldYield = deadline.timeRemaining() < 1
  }

  // 所有的任务已经完成，wipRoot树也在
  if (!nextUnitOfWork && wipRoot) {
    commitRoot()
  }
  requestIdleCallback(workLoop)
}

function commitRoot() {
  // 一次性生成页面
  // 递归挂载
  // 先删除
  deletions.forEach(commitWork)
  commitWork(wipRoot.child)
  currentRoot = wipRoot
  wipRoot = null
}

const isEvent = key => key.startsWith("on")
const isProperty = key =>
  key !== "children" && !isEvent(key)
const isNew = (prev, next) => key =>
  prev[key] !== next[key]
const isGone = (prev, next) => key => !(key in next)


function updateDom(dom, prevProps, nextProps) {
  // console.log('更新')
  Object.keys(prevProps)
  .filter(isEvent)
  .filter(
    key =>
      !(key in nextProps) ||
      isNew(prevProps, nextProps)(key)
  )
  .forEach(name => {
    const eventType = name
      .toLowerCase()
      .substring(2)
    dom.removeEventListener(
      eventType,
      prevProps[name]
    )
  })

  // Add event listeners
  Object.keys(nextProps)
    .filter(isEvent)
    .filter(isNew(prevProps, nextProps))
    .forEach(name => {
      const eventType = name
        .toLowerCase()
        .substring(2)
      dom.addEventListener(
        eventType,
        nextProps[name]
      )
    })

  // Remove old properties
  Object.keys(prevProps)
    .filter(isProperty)
    .filter(isGone(prevProps, nextProps))
    .forEach(name => {
      dom[name] = ""
    })

  // Set new or changed properties
  Object.keys(nextProps)
    .filter(isProperty)
    .filter(isNew(prevProps, nextProps))
    .forEach(name => {
      dom[name] = nextProps[name]
    })
}

// 挂载节点，修改节点
function commitWork(fiber) {
  if (!fiber) {
    return;
  }

  let domParentFiber = fiber.parent
  while (!domParentFiber.dom) {
    // 函数组件，没有dom，其子节点应该挂再在其父节点的有dom的fiber节点上
    domParentFiber = domParentFiber.parent
  }
  const domParent = domParentFiber.dom

  if (fiber.effectTag == 'PLACEMENT' && fiber.dom !== null) {
    domParent.appendChild(fiber.dom)
  }
  else if (fiber.effectTag === 'UPDATE' && fiber.dom != null) {
    updateDom(
      fiber.dom,
      fiber.alternate.props,
      fiber.props
    )
  }
  else if (fiber.effectTag === 'DELETION') {
    commitDeletion(fiber, domParent)
  }
  commitWork(fiber.child)
  commitWork(fiber.sibling)
}

// 移除节点也同样需要找到该 fiber 下第一个有 DOM 节点的 fiber 节点
function commitDeletion(fiber, domParent) {
  // 递归，如果是真实节点，则直接删除，如果不是，则往下递归
  if (fiber.dom) {
    domParent.removeChild(fiber.dom)
  } else {
    commitDeletion(fiber.child, domParent)
  }
}

function createDom(fiber) {
  const dom = fiber.type == "TEXT_ELEMENT"
    ? document.createTextNode("")
    : document.createElement(fiber.type);

    updateDom(dom, {}, fiber.props);
  return dom
}

function performUnitOfWork(fiber) {

  // 判断函数组件
  const isFunctionComponent = fiber.type instanceof Function
  if (isFunctionComponent) {
    // 函数组件
    updateFunctionComponent(fiber)
  } 
  else {
    // 非函数组件
    updateHostComponent(fiber)
  }

  // if (!fiber.dom) {
  //   fiber.dom = createDom(fiber)
  // }

  // // 创建fiber树结构
  // const elements = fiber.props.children

  // // 创建fiber节点 
  // reconcileChildren(fiber, elements)



  // 找出下一个节点
  if (fiber.child) {
    return fiber.child
  }
  let nextFiber = fiber
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling
    }
    nextFiber = nextFiber.parent
  }
}



function updateFunctionComponent(fiber) {
  // TODO 创建函数组件结构

  wipFiber = fiber
  hookIndex = 0
  wipFiber.hooks = []

  const children = [fiber.type(fiber.props)]
  reconcileChildren(fiber, children)
}

function updateHostComponent(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber)
  }
  reconcileChildren(fiber, fiber.props.children)
}

function reconcileChildren(wipFiber, elements) {
  // 创建fiber节点
  let index = 0
  let prevSibling = null

  let oldFiber = wipFiber.alternate && wipFiber.alternate.child

  while (index < elements.length || oldFiber != null) {
    const element = elements[index]

    let newFiber = null

    // 比较
    const sameType = oldFiber && element && element.type == oldFiber.type;

    if (sameType) {
      // Todo update the node 
      newFiber = {
        type: oldFiber.type,
        props: element.props,
        dom: oldFiber.dom,  // 复用之前的节点
        parent: wipFiber,
        alternate: oldFiber,
        effectTag: 'UPDATE'
      }
    }
    if (element && !sameType) {
      // todo add this  node 
      newFiber = {
        type: element.type,
        props: element.props,
        dom: null,
        parent: wipFiber,
        alternate: null,
        effectTag: 'PLACEMENT'
      }
    }
    if (oldFiber && !sameType) {
      // todo detele this node
      oldFiber.effectTag = "DELETION";
      deletions.push(oldFiber);
    }

    // const newFiber = {
    //   type: element.type,
    //   props: element.props,
    //   parent: wipFiber,
    //   dom: null,
    // }

    if (oldFiber) {
      oldFiber = oldFiber.sibling
    }

    if (index === 0) {
      wipFiber.child = newFiber
    } else {
      prevSibling.sibling = newFiber
    }

    prevSibling = newFiber
    index++
  }
}

requestIdleCallback(workLoop)

let wipFiber = null
let hookIndex = null

function useState(initial) {
  // TODO useState
  const oldHook =
    wipFiber.alternate &&
    wipFiber.alternate.hooks &&
    wipFiber.alternate.hooks[hookIndex]
  const hook = {
    state: oldHook ? oldHook.state : initial,
    queue:[]
  }

  const actions = oldHook ? oldHook.queue : []
  actions.forEach(action => {
    hook.state = action(hook.state)
  })

  const setState = action => {
    hook.queue.push(action);
    wipRoot = {
      dom: currentRoot.dom,
      props: currentRoot.props,
      alternate: currentRoot
    };
    nextUnitOfWork = wipRoot;
    deletions = [];
  }

  wipFiber.hooks.push(hook)
  hookIndex++
  // console.log(hook.state,setState)
  return [hook.state,setState]
}


const Didact = {
  createElement,
  render,
  useState
};

/** @jsxRuntime classic */
/** @jsx Didact.createElement */
// const element = (
//   <div style="background: salmon">
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//     <h1>Hello World</h1>
//     <h2 style="text-align:right">from Didact</h2>
//   </div>
// );

function Counter() {
  const [state, setState] = Didact.useState(1)
  return (
    <h1 id='h1' onClick={() => setState(c => c + 1)}>
      Count: {state}
    </h1>
  )
}
const element2 = <Counter />

console.log(element2)
const container = document.getElementById("root");
Didact.render(element2, container);